﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using System.Xml;

namespace AppDesigner.XamlDom
{
    public class XamlObject : XamlPropertyValue
    {
        #region Fields

        private XamlDocument _xamlDocument; //对应的Xaml文档
        private XmlElement _element;// 解析的xml节点
        private Type _elementType;//反射的类型
        private object _instance;//反射出来的实例

        private List<XamlProperty> properties = new List<XamlProperty>();
        private XamlObject _parentObject;
        private string _contentPropertyName;
        private XmlAttribute _xmlAttribute;
        private MarkupExtensionWrapper _wrapper;

        #endregion

        #region Constructor

        public XamlObject(XamlDocument xamlDocument, XmlElement element, Type elementType, object instance)
        {
            _xamlDocument = xamlDocument;
            _element = element;
            _elementType = elementType;
            _instance = instance;

            //获取contentProperty或者说是defaultProperty的名称
            var contentAttributes = elementType.GetCustomAttributes(typeof(ContentPropertyAttribute), true) as ContentPropertyAttribute[];
            if (contentAttributes != null && contentAttributes.Length > 0)
            {
                _contentPropertyName = contentAttributes[0].Name;
            }

            ServiceProvider = new XamlObjectServiceProvider(this);
            CreateWrapper();
        }

        #endregion

        #region Properties

        /// <summary>
        /// 实例属性
        /// </summary>
        public object Instance
        {
            get
            {
                return _instance;
            }
        }

        /// <summary>
        /// 是否有错误
        /// </summary>
        public bool HasErrors { get; set; }

        /// <summary>
        /// 解析的XmlElement
        /// </summary>
        public XmlElement XmlElement { get; set; }

        /// <summary>
        /// 服务提供者
        /// </summary>
        public XamlObjectServiceProvider ServiceProvider { get; set; }

        public bool IsMarkupExtension { get; set; }

        public List<XamlProperty> Properties
        {
            get
            {
                return properties;
            }
        }

        public Type ElementType
        {
            get
            {
                return _elementType;
            }
        }

        public XamlObject ParentObject
        {
            get { return _parentObject; }
            set { _parentObject = value; }
        }

        public string ContentPropertyName
        {
            get
            {
                return _contentPropertyName;
            }
        }

        public XmlAttribute XmlAttribute
        {
            get { return _xmlAttribute; }
            set
            {
                _xmlAttribute = value;
                _element = VirtualAttachTo(XmlElement, value.OwnerElement);
            }
        }
        #endregion

        #region Implements XamlPropertyValue

        /// <summary>
        /// 为某个XamlPropertyInfo 获取值,
        /// 其实是借助XamlParser来用text 解析
        /// </summary>
        /// <param name="targetProperty"></param>
        /// <returns></returns>
        public override object GetValueFor(XamlPropertyInfo targetProperty)
        {
            if (IsMarkupExtension)
            {
                var value = ProvideValue();
                if (value is string && targetProperty != null && targetProperty.ReturnType != typeof(string))
                {
                    return XamlParser.Instance.CreateObjectFromAttributeText((string)value, targetProperty, this);
                }
                return value;
            }
            else
            {
                return _instance;
            }
        }

        /// <summary>
        /// 从父节点移除节点
        /// </summary>
        public override void RemoveNodeFromParent()
        {
            if (XmlAttribute != null)
            {
                XmlAttribute.OwnerElement.RemoveAttribute(XmlAttribute.Name);
                _xmlAttribute = null;
            }
            else
            {
                if (!UpdateXmlAttribute(false))
                {
                    _element.ParentNode.RemoveChild(_element);
                }
            }
        }

        public override void AddNodeTo(XamlProperty property)
        {
            if (!UpdateXmlAttribute(true))
            {
                property.AddChildNodeToProperty(_element);
            }
            UpdateMarkupExtensionChain();
        }

        public override XmlNode GetNodeForCollection()
        {
            return _element;
        }

        public XamlDocument OwnerDocument
        {
            get { return _xamlDocument; }
        }

        #endregion

        #region Method

        //如果是绑定或者资源那么需要用这个wrapper来提供值
        private void CreateWrapper()
        {
            if (Instance is Binding)
            {
                _wrapper = new BindingWrapper();
            }
            else if (Instance is StaticResourceExtension)
            {
                _wrapper = new StaticResourceWrapper();
            }
            if (_wrapper != null)
            {
                _wrapper.XamlObject = this;
            }
        }

        /// <summary>
        /// 获取值,应该是默认对应于DefaultProperty
        /// </summary>
        /// <returns></returns>
        private object ProvideValue()
        {
            if (_wrapper != null)
            {
                return _wrapper.ProvideValue();
            }
            return (Instance as MarkupExtension).ProvideValue(ServiceProvider);
        }

        private XmlElement VirtualAttachTo(XmlElement e, XmlElement target)
        {
            var prefix = target.GetPrefixOfNamespace(e.NamespaceURI);
            XmlElement newElement = e.OwnerDocument.CreateElement(prefix, e.LocalName, e.NamespaceURI);

            foreach (XmlAttribute attrbute in target.Attributes)
            {
                if (attrbute.Prefix == "xmlns" || attrbute.Name == "xmlns")
                {
                    newElement.Attributes.Append(attrbute.Clone() as XmlAttribute);
                }
            }

            while (e.HasChildNodes)
            {
                newElement.AppendChild(e.FirstChild);
            }
            XmlAttributeCollection ac = e.Attributes;
            while (ac.Count > 0)
            {
                newElement.Attributes.Append(ac[0]);
            }
            return newElement;
        }
        /// <summary>
        /// 添加XamlProperty
        /// </summary>
        /// <param name="property"></param>
        public void AddProperty(XamlProperty property)
        {
#if DEBUG
            if (!property.IsAttached)
            {
                foreach (XamlProperty p in properties)
                {
                    if (!p.IsAttached && p.PropertyName == property.PropertyName)
                    {
                        throw new InvalidOperationException("duplicate property.");
                    }
                }
            }
#endif
            properties.Add(property);
        }

        public string GetXamlAttribute(string name)
        {
            return _element.GetAttribute(name, XamlConstants.XamlNamespace);
        }

        public override void OnParentPropertyChanged()
        {
            _parentObject = (ParentProperty != null) ? ParentProperty.ParentObject : null;
            base.OnParentPropertyChanged();
        }

        public void OnPropertyChanged(XamlProperty property)
        {
            UpdateXmlAttribute(false);
            UpdateMarkupExtensionChain();
        }

        private void UpdateMarkupExtensionChain()
        {
            var obj = this;
            while (obj != null && obj.IsMarkupExtension)
            {
                obj.ParentProperty.UpdateValueOnInstance();
                obj = obj.ParentObject;
            }
        }

        private bool UpdateXmlAttribute(bool force)
        {
            XamlObject holder = FindXmlAttributeHolder();
            if (holder == null && force && IsMarkupExtension)
            {
                holder = this;
            }
            if (holder != null && MarkupExtensionPrinter.CanPrint(holder))
            {
                var s = MarkupExtensionPrinter.Print(holder);
                holder.XmlAttribute = holder.ParentProperty.SetAttribute(s);
                return true;
            }
            return false;
        }

        private XamlObject FindXmlAttributeHolder()
        {
            XamlObject obj = this;
            while (obj != null && obj.IsMarkupExtension)
            {
                if (obj.XmlAttribute != null)
                {
                    return obj;
                }
                obj = obj.ParentObject;
            }
            return obj;
        }

        public string GetNameForMarkupExtension()
        {
            return XmlElement.Name;
        }

        #endregion
        
    }

    #region MarkupExtensionWrapper

    abstract class MarkupExtensionWrapper
    {
        public XamlObject XamlObject { get; set; }
        public abstract object ProvideValue();
    }

    class BindingWrapper : MarkupExtensionWrapper
    {
        public override object ProvideValue()
        {
            var target = XamlObject.Instance as Binding;
            var binding = new Binding();
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(target))
            {
                if (pd.IsReadOnly)
                {
                    continue;
                }
                try
                {
                    var val1 = pd.GetValue(binding);
                    var val2 = pd.GetValue(target);
                    if (object.Equals(val1, val2))
                    {
                        continue;
                    }
                    pd.SetValue(binding, val2);
                }
                catch { }
            }
            return binding.ProvideValue(XamlObject.ServiceProvider);
        }
    }

    class StaticResourceWrapper : MarkupExtensionWrapper
    {

        public override object ProvideValue()
        {
            var target = XamlObject.Instance as StaticResourceExtension;
            return XamlObject.ServiceProvider.Resolver.FindResource(target.ResourceKey);
        }
    }

    #endregion
}
